package io.gitee.thinkbungee.crud.mongo.criteria;

import cn.hutool.core.collection.CollUtil;
import io.gitee.thinkbungee.crud.mongo.reflection.SerializableFunction;
import io.gitee.thinkbungee.crud.mongo.utils.FunctionResolveUtils;
import io.gitee.thinkbungee.utils.SatelliteUtils;
import org.springframework.data.mongodb.core.query.Criteria;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.regex.Pattern;

/**
 * 提供2种不同的方式
 * 如：
 * 1.字符串方式
 * .eq(string,string)
 * 2.函数表达式方式
 * .eq(function,string)
 *
 * @author chenyichang
 * @date 2022/4/20
 */
public class CriteriaWrapper {

    /**
     * true：查询条件用and连接
     * false：查询条件用or连接
     */
    boolean andLink = true;

    /**
     * 查询条件
     */
    protected Criteria criteria;

    /**
     * 查询条件
     */
    List<Criteria> list = new ArrayList<>();

    /**
     * 返回criteria
     */
    public Criteria build() {
        criteria = new Criteria();
        //list不为空，将条件拼装起来
        if (CollUtil.isNotEmpty(list)) {
            if (andLink) {
                criteria.andOperator(listToArray(list));
            } else {
                criteria.orOperator(listToArray(list));
            }
        }
        return criteria;
    }

    private Criteria[] listToArray(List<Criteria> list) {
        return list.toArray(new Criteria[list.size()]);
    }


    //========================相关查询方法=============================//

    /**
     * 等于
     *
     * @param column 字段函数
     * @param value  值
     * @param <T>    函数参数泛型
     * @param <R>    函数返回值泛型
     * @return
     */
    public <T, R> CriteriaWrapper eq(SerializableFunction<T, R> column, Object value) {
        return eq(FunctionResolveUtils.resolve(column), value);
    }

    public CriteriaWrapper eq(String column, Object value) {
        list.add(Criteria.where(column).is(value));
        return this;
    }

    /**
     * 不等于
     *
     * @param column 字段函数
     * @param value  值
     * @param <T>    函数参数泛型
     * @param <R>    函数返回值泛型
     * @return
     */
    public <T, R> CriteriaWrapper ne(SerializableFunction<T, R> column, Object value) {
        return ne(FunctionResolveUtils.resolve(column), value);
    }

    public CriteriaWrapper ne(String column, Object value) {
        list.add(Criteria.where(column).ne(value));
        return this;
    }

    /**
     * 小于
     *
     * @param column 字段函数
     * @param value  值
     * @param <T>    函数参数泛型
     * @param <R>    函数返回值泛型
     * @return
     */
    public <T, R> CriteriaWrapper lt(SerializableFunction<T, R> column, Object value) {
        return lt(FunctionResolveUtils.resolve(column), value);
    }

    public CriteriaWrapper lt(String column, Object value) {
        list.add(Criteria.where(column).lt(value));
        return this;
    }

    /**
     * 小于等于
     *
     * @param column 字段函数
     * @param value  值
     * @param <T>    函数参数泛型
     * @param <R>    函数返回值泛型
     * @return
     */
    public <T, R> CriteriaWrapper lte(SerializableFunction<T, R> column, Object value) {
        return lte(FunctionResolveUtils.resolve(column), value);
    }

    public CriteriaWrapper lte(String column, Object value) {
        list.add(Criteria.where(column).lte(value));
        return this;
    }

    /**
     * 大于
     *
     * @param column 字段函数
     * @param value  值
     * @param <T>    函数参数泛型
     * @param <R>    函数返回值泛型
     * @return
     */
    public <T, R> CriteriaWrapper gt(SerializableFunction<T, R> column, Object value) {
        return gt(FunctionResolveUtils.resolve(column), value);
    }

    public CriteriaWrapper gt(String column, Object value) {
        list.add(Criteria.where(column).gt(value));
        return this;
    }

    /**
     * 大于等于
     *
     * @param column 字段函数
     * @param value  值
     * @param <T>    函数参数泛型
     * @param <R>    函数返回值泛型
     * @return
     */
    public <T, R> CriteriaWrapper gte(SerializableFunction<T, R> column, Object value) {
        return gte(FunctionResolveUtils.resolve(column), value);
    }

    public CriteriaWrapper gte(String column, Object value) {
        list.add(Criteria.where(column).gte(value));
        return this;
    }

    /**
     * 包含
     * <p>
     * mongo的 $all
     * https://www.mongodb.com/docs/manual/reference/operator/query/all/
     *
     * @param column 字段函数
     * @param value  值
     * @param <T>    函数参数泛型
     * @param <R>    函数返回值泛型
     * @return
     */
    public <T, R> CriteriaWrapper contain(SerializableFunction<T, R> column, Object value) {
        return contain(FunctionResolveUtils.resolve(column), value);
    }

    public CriteriaWrapper contain(String column, Object value) {
        list.add(Criteria.where(column).all(value));
        return this;
    }

    /**
     * 包含，用or连接
     *
     * @param column 字段函数
     * @param value  值
     * @param <T>    函数参数泛型
     * @param <R>    函数返回值泛型
     * @return
     */
    public <T, R> CriteriaWrapper containOr(SerializableFunction<T, R> column, Collection<?> value) {
        return containOr(FunctionResolveUtils.resolve(column), value);
    }

    public CriteriaWrapper containOr(String column, Collection<?> value) {
        CriteriaOrWrapper criteriaOrWrapper = new CriteriaOrWrapper();
        value.forEach(v -> criteriaOrWrapper.contain(column, v));
        list.add(criteriaOrWrapper.build());
        return this;
    }

    /**
     * 包含，用or连接
     *
     * @param column 字段函数
     * @param values 值
     * @param <T>    函数参数泛型
     * @param <R>    函数返回值泛型
     * @return
     */
    public <T, R> CriteriaWrapper containOr(SerializableFunction<T, R> column, Object[] values) {
        return containOr(column, Arrays.asList(values));
    }

    public CriteriaWrapper containOr(String column, Object[] values) {
        return containOr(column, Arrays.asList(values));
    }

    /**
     * 包含，以and连接
     * 当 {@link CriteriaWrapper#contain(SerializableFunction, Object)}
     * Object类型为集合，2个方法一样
     *
     * @param column 字段函数
     * @param value  值
     * @param <T>    函数参数泛型
     * @param <R>    函数返回值泛型
     * @return
     */
    public <T, R> CriteriaWrapper containAnd(SerializableFunction<T, R> column, Collection<?> value) {
        return containAnd(FunctionResolveUtils.resolve(column), value);
    }

    public CriteriaWrapper containAnd(String column, Collection<?> value) {
        list.add(Criteria.where(column).all(value));
        return this;
    }

    /**
     * 包含，以and连接
     *
     * @param column 字段函数
     * @param values 值
     * @param <T>    函数参数泛型
     * @param <R>    函数返回值泛型
     * @return
     */
    public <T, R> CriteriaWrapper containAnd(SerializableFunction<T, R> column, Object[] values) {
        return containAnd(column, Arrays.asList(values));
    }

    public CriteriaWrapper containAnd(String column, Object[] values) {
        return containAnd(column, Arrays.asList(values));
    }

    /**
     * 类似
     *
     * @param column 字段函数
     * @param value  值
     * @param <T>    函数参数泛型
     * @param <R>    函数返回值泛型
     * @return
     */
    public <T, R> CriteriaWrapper like(SerializableFunction<T, R> column, String value) {
        return like(FunctionResolveUtils.resolve(column), value);
    }

    public CriteriaWrapper like(String column, String value) {
        //使用不区分大小写模式正则匹配
        Pattern pattern = Pattern.compile("^.*" + SatelliteUtils.repalceSpecialWord(value) + ".*$",
                Pattern.CASE_INSENSITIVE);
        list.add(Criteria.where(column).regex(pattern));
        return this;
    }

    /**
     * 在其中
     *
     * @param column 字段函数
     * @param values 值
     * @param <T>    函数参数泛型
     * @param <R>    函数返回值泛型
     * @return
     */
    public <T, R> CriteriaWrapper in(SerializableFunction<T, R> column, Collection<?> values) {
        return in(FunctionResolveUtils.resolve(column), values);
    }

    public CriteriaWrapper in(String column, Collection<?> values) {
        list.add(Criteria.where(column).in(values));
        return this;
    }

    /**
     * 在其中
     *
     * @param column 字段函数
     * @param values 值
     * @param <T>    函数参数泛型
     * @param <R>    函数返回值泛型
     * @return
     */
    public <T, R> CriteriaWrapper in(SerializableFunction<T, R> column, Object[] values) {
        return in(column, Arrays.asList(values));
    }

    public CriteriaWrapper in(String column, Object[] values) {
        return in(column, Arrays.asList(values));
    }

    /**
     * 不在其中
     *
     * @param column 字段函数
     * @param values 值
     * @param <T>    函数参数泛型
     * @param <R>    函数返回值泛型
     * @return
     */
    public <T, R> CriteriaWrapper nin(SerializableFunction<T, R> column, Collection<?> values) {
        return nin(FunctionResolveUtils.resolve(column), values);
    }

    public CriteriaWrapper nin(String column, Collection<?> values) {
        list.add(Criteria.where(column).nin(values));
        return this;
    }

    /**
     * 不在其中
     *
     * @param column 字段函数
     * @param values 值
     * @param <T>    函数参数泛型
     * @param <R>    函数返回值泛型
     * @return
     */
    public <T, R> CriteriaWrapper nin(SerializableFunction<T, R> column, Object[] values) {
        return nin(column, Arrays.asList(values));
    }

    public CriteriaWrapper nin(String column, Object[] values) {
        return nin(column, Arrays.asList(values));
    }

    /**
     * 列为null
     *
     * @param column 字段函数
     * @param <T>    函数参数泛型
     * @param <R>    函数返回值泛型
     * @return
     */
    public <T, R> CriteriaWrapper isNull(SerializableFunction<T, R> column) {
        return isNull(FunctionResolveUtils.resolve(column));
    }

    public CriteriaWrapper isNull(String column) {
        list.add(Criteria.where(column).is(null));
        return this;
    }

    /**
     * 等于
     *
     * @param column 字段函数
     * @param <T>    函数参数泛型
     * @param <R>    函数返回值泛型
     * @return
     */
    public <T, R> CriteriaWrapper isNotNull(SerializableFunction<T, R> column) {
        return isNotNull(FunctionResolveUtils.resolve(column));
    }

    public CriteriaWrapper isNotNull(String column) {
        list.add(Criteria.where(column).ne(null));
        return this;
    }

    /**
     * 数组查询
     *
     * @param arr    数组字段
     * @param column 字段函数
     * @param value  值
     * @param <T>    函数参数泛型
     * @param <R>    函数返回值泛型
     * @return
     */
    public <T, R> CriteriaWrapper findArray(String arr, SerializableFunction<T, R> column, Object value) {
        return findArray(arr, FunctionResolveUtils.resolve(column), value);
    }

    public CriteriaWrapper findArray(String arr, String column, Object value) {
        list.add(Criteria.where(arr)
                .elemMatch(Criteria.where(column).is(value)));
        return this;
    }

    /**
     * 数组模糊查询
     *
     * @param arr    数组字段
     * @param column 字段函数
     * @param value  值
     * @param <T>    函数参数泛型
     * @param <R>    函数返回值泛型
     * @return
     */
    public <T, R> CriteriaWrapper findArrayLike(String arr, SerializableFunction<T, R> column, String value) {
        return findArrayLike(arr, FunctionResolveUtils.resolve(column), value);
    }

    public CriteriaWrapper findArrayLike(String arr, String column, String value) {
        Pattern pattern = Pattern.compile("^.*" + SatelliteUtils.repalceSpecialWord(value) + ".*$",
                Pattern.CASE_INSENSITIVE);
        list.add(Criteria.where(arr)
                .elemMatch(Criteria.where(column).regex(pattern)));
        return this;
    }

}
